#include "defines.h"

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

class DGraph
{
	private Graphics g;
	private DSmallFont fontLow;
	private EMidFont fontHig;
	
	#ifndef REMOVE_IMAGES
	private Image bb_img;
	#endif
	private Graphics bb_g;
	
	private int lastClipX = 0;
	private int lastClipY = 0;
	private int lastClipW = 0;
	private int lastClipH = 0;
	
	private int savedClipX = 0;
	private int savedClipY = 0;
	private int savedClipW = 0;
	private int savedClipH = 0;
	
	private int lastColor = 0;
	
	private int scr_w = 0;
	private int scr_h = 0;
	private int scr_min = 0;
	
	public boolean useLowFont = false;

	public boolean useBackBuffer = false;
	
	public static final int FONT_SMALL 	= 1;
	public static final int FONT_BIG 	= 2;
	public static final int MIN_RES 	= 160;
		
	public DGraph(Graphics _g, int s_w, int s_h, boolean _bb)
	{
		g = _g;
		scr_w = s_w;
		scr_h = s_h;
		scr_min = MIN(s_w, s_h);
		setClip(0, 0, scr_w, scr_h);
		
		if (scr_min < MIN_RES)
		{
			useLowFont = true;
		}
		
		useBackBuffer = _bb;
		
		if(useBackBuffer)
		{
			#ifndef REMOVE_IMAGES
			bb_img = Image.createImage(scr_w, scr_h);
			bb_g = bb_img.getGraphics();
			#else
			bb_g = g;
			#endif
			if (!useLowFont)
			{
				fontHig = new EMidFont(bb_g, scr_h);
			}
			else 
			{
				fontLow = new DSmallFont(bb_g, scr_h);
			}
		}
		else
		{
			if (!useLowFont)
			{
				fontHig = new EMidFont(g, scr_h);
			}
			else
			{
				fontLow = new DSmallFont(g, scr_h);
			}
		}
	}
	
	void setGraphics(Graphics _g)
	{
		g = _g;
	}
	
	Graphics getFgGraphics()
	{
		return g;
	}
	
	Graphics getBackBufferGraphics()
	{
		return bb_g;
	}
	
	Image getBackBufferImage()
	{
		#ifndef REMOVE_IMAGES
		return bb_img;
		#else
		return null;
		#endif
	}
	
	// TODO: wrap this method better, as some cellphones doesn't have support for
	// transformation flags while using drawRegion
	void drawRegion(Image srcImg, int x_src, int y_src, int w, int h, int transform, int x_dest, int y_dest, int anchor)
	{
		if(!useBackBuffer)
		{
			g.drawRegion(srcImg, x_src, y_src, w, h, transform, x_dest, y_dest, anchor);
		}
		else
		{
			bb_g.drawRegion(srcImg, x_src, y_src, w, h, transform, x_dest, y_dest, anchor);
		}
	}
	
	void drawImage(Image srcImg, int x, int y, int anchor)
	{
		try
		{
			#ifndef REMOVE_IMAGES
			if(!useBackBuffer)
			{
				g.drawImage(srcImg, x, y, anchor);
			}
			else
			{
				bb_g.drawImage(srcImg, x, y, anchor);
			}
			#endif
		}
		catch (NullPointerException e)
		{
			cGame.error = new String ("NullPointer " + __FILE__ + " " + __LINE__);
		}
	}
	
	void fillArc(int x, int y, int w, int h, int start_ang, int end_ang)
	{
		if(!useBackBuffer)
		{
			g.fillArc(x, y, w, h, start_ang, end_ang);
		}
		else
		{
			bb_g.fillArc(x, y, w, h, start_ang, end_ang);
		}
	}
	
	void setColor(int color)
	{
		if(!useBackBuffer)
		{
			g.setColor(color);
		}
		else
		{
			bb_g.setColor(color);
		}
			
		lastColor = color;
	}
	
	int getColor()
	{
		return lastColor;
	}
	
	void setClip(int x, int y, int w, int h)
	{
		if(!useBackBuffer)
		{
			g.setClip(x, y, w, h);
		}
		else
		{
			bb_g.setClip(x, y, w, h);
		}
			
		lastClipX = x;
		lastClipY = y;
		lastClipW = w;
		lastClipH = h;
	}
	
	void saveClip()
	{
		savedClipX = lastClipX;
		savedClipY = lastClipY;
		savedClipW = lastClipW;
		savedClipH = lastClipH;
	}
	
	void restoreClip()
	{
		setClip(savedClipX, savedClipY, savedClipW, savedClipH);
	}
	
	int getClipX()
	{
		return lastClipX;
	}
	
	int getClipY()
	{
		return lastClipY;
	}
	
	int getClipW()
	{
		return lastClipW;
	}
	
	int getClipH()
	{
		return lastClipH;
	}
	
	void _drawRect(int x, int y, int w, int h)
	{
		if(!useBackBuffer)
		{
			g.drawRect(x, y, w, h);
		}
		else
		{
			bb_g.drawRect(x, y, w, h);
		}
	}
	
	void drawRect(int x, int y, int w, int h)
	{
		if((x > scr_w) || (y > scr_h) || ((x + w) < 0) || ((y + h) < 0))
		{
			return;
		}
		
		if(x < 0)
		{
			w = w + x;
			x = 0;
		}
		
		if(y < 0)
		{
			h = h + y;
			y = 0;
		}
		
		if((x + w) > scr_w)
		{
			w = scr_w - x;
		}
		
		if((y + h) > scr_h)
		{
			h = (scr_h - y) + 1;
		}
		
		_drawRect(x, y, w, h);
	}
	
	void _fillRect(int x, int y, int w, int h)
	{
		if(!useBackBuffer)
		{
			g.fillRect(x, y, w, h);
		}
		else
		{
			bb_g.fillRect(x, y, w, h);
		}
	}
	
	void fillRect(int x, int y, int w, int h)
	{
		if((x > scr_w) || (y > scr_h) || ((x + w) < 0) || ((y + h) < 0))
		{
			return;
		}
		
		if(x < 0)
		{
			w = w + x;
			x = 0;
		}
		
		if(y < 0)
		{
			h = h + y;
			y = 0;
		}
		
		if((x + w) > scr_w)
		{
			w = scr_w - x;
		}
		
		if((y + h) > scr_h)
		{
			h = scr_h - y;
		}
		
		_fillRect(x, y, w, h);
	}
	
	void drawLine(int x1, int y1, int x2, int y2)
	{
		if(!useBackBuffer)
		{
			g.drawLine(x1, y1, x2, y2);
		}
		else
		{
			bb_g.drawLine(x1, y1, x2, y2);
		}
	}
	
	void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3)
	{
		if(!useBackBuffer)
		{
			g.fillTriangle(x1, y1, x2, y2, x3, y3);
		}
		else
		{
			bb_g.fillTriangle(x1, y1, x2, y2, x3, y3);
		}
	}
	
	void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
	{
		if(!useBackBuffer)
		{
			g.drawArc(x, y, width, height, startAngle, arcAngle);
		}
		else
		{
			bb_g.drawArc(x, y, width, height, startAngle, arcAngle);
		}
	}
	
	void drawRGB(int[] rgbData, int offset, int scanlength, int x, int y, int width, int height, boolean processAlpha)
	{
		#ifndef REMOVE_IMAGES
		if(!useBackBuffer)
		{
			g.drawRGB(rgbData, offset, scanlength, x, y, width, height, processAlpha);
		}
		else
		{
			bb_g.drawRGB(rgbData, offset, scanlength, x, y, width, height, processAlpha);
		}
		#endif
	}
	
	void paintBuffer()
	{
		#ifndef REMOVE_IMAGES
		if(useBackBuffer)
		{
			g.drawImage(bb_img, 0, 0, 0);			
		}
		#endif
	}
	
	private int alphaBlend(int fgPixel, int bgPixel, int alphaLevel)
	{
		return (((fgPixel * 0xFF) + (bgPixel * (0xFF - alphaLevel))) / 0xFF);
	}
	
	void fillRoundRect(int x, int y, int w, int h, int arcH, int arcW)
	{
		int lastY = 0;
	
		for(int i = arcH; i >= 0; i--)
		{
			fillRect(x + (arcW / (arcH - i + 1)), y + (arcH - i), w - (arcW / (arcH - i + 1)) * 2, 1);
			lastY = y + (arcH - i);
		}
		
		lastY++;
		
		fillRect(x, lastY, w, h - arcH * 2);
		
		lastY--;
				
		for(int j = 0; j < arcH + 1; j++)
		{
			fillRect(x + (arcW / (arcH - j + 1)), lastY + h - arcH * 2 + j, w - (arcW / (arcH - j + 1)) * 2, 1);
		}
	}
	
	void drawRoundButton(int x, int y, int w, int h, int startColor, int endColor)
	{
		drawRoundButton("", x, y, w, h, startColor, endColor, 0x000000);
	}
	
	void drawRoundButton(String str, int x, int y, int w, int h, int startColor, int endColor, int fontColor)
	{
		if(h >= 4)
		{
			int rs = startColor >>>	16;
			int gs = (startColor >>> 8) & 0x000000FF;
			int bs = startColor & 0x000000FF;
					
			int rRatio = ((rs - (endColor >>> 16)) / (h / 4)) * -1;
			int gRatio = ((gs - ((endColor >>> 8) & 0x000000FF)) / (h / 4)) * -1;
			int bRatio = ((bs - (endColor & 0x000000FF)) / (h / 4)) * -1;
			
			for(int i = 0; i < h / 4; i++)
			{
				g.setColor((rs<<16) | (gs<<8) | (bs));
				rs+=rRatio;
				gs+=gRatio;
				bs+=bRatio;
				
				fillRoundRect(x + i, y + i, w - i * 2, h - i * 2, h * 10 / 100, w * 10 / 100);
			}
		}
		else
		{
			g.setColor(startColor);
			fillRoundRect(x, y, w, h, 1, 1);
		}
		
		setFontColor(fontColor);
		
		if(getStringWidth(str) < w)
		{
			drawString(str, x + w / 2, y + h / 2 + getFontHeight() / 2,DSmallFont.BOTTOM | DSmallFont.HCENTER);
		}
		else
		{
			String[] buffer = split(str, " ");
			int lines = buffer.length;
			int lastIndex = 0;
			
			for(int k = 0; k < buffer.length - 1; k++)
			{
				if(getStringWidth(buffer[lastIndex] + " " + buffer[k + 1]) < w)
				{
					buffer[lastIndex] = buffer[lastIndex] + " " + buffer[k + 1];
					lines--;
				}
				else
				{
					lastIndex++;
					buffer[lastIndex] = buffer[k + 1];
				}
			}
			
			for(int j = 0; j < lines; j++)
			{
				drawString(buffer[j], x + w / 2, y + (h / (lines+1)) + (h / (lines+1)) * j + getFontHeight() / 2, DSmallFont.BOTTOM | DSmallFont.HCENTER);
			}
		}
	}
	
	void drawRectFadeButton(int x, int y, int w, int h, int startColor, int endColor)
	{
		drawRectFadeButton("", x, y, w, h, startColor, endColor, 0x000000);
	}
	
	void drawRectFadeButton(String str, int x, int y, float w, int h, int startColor, int endColor, int fontColor)
	{
		drawRectFadeButton(str, x, y, (int)w, h, startColor, endColor, fontColor);
	}
	
	void drawRectFadeButton(String str, int x, int y, int w, int h, int startColor, int endColor, int fontColor)
	{
		drawGFRect(x, y, w, h, startColor, endColor);
		setColor(fontColor);
		drawRect(x, y, w, h);
		
		setFontColor(fontColor);
		
		if(getStringWidth(str) < w)
		{
			drawString(str, x + w / 2, y + h / 2 + getFontHeight() / 2,DSmallFont.BOTTOM | DSmallFont.HCENTER);
		}
		else
		{
			String[] buffer = split(str, " ");
			int lines = buffer.length;
			int lastIndex = 0;
			
			for(int k = 0; k < buffer.length - 1; k++)
			{
				if(getStringWidth(buffer[lastIndex] + " " + buffer[k + 1]) < w)
				{
					buffer[lastIndex] = buffer[lastIndex] + " " + buffer[k + 1];
					lines--;
				}
				else
				{
					lastIndex++;
					buffer[lastIndex] = buffer[k + 1];
				}
			}
			
			for(int j = 0; j < lines; j++)
			{
				drawString(buffer[j], x + w / 2, y + (h / (lines+1)) + (h / (lines+1)) * j + getFontHeight() / 2, DSmallFont.BOTTOM | DSmallFont.HCENTER);
			}
		}
	}
	
	void drawGFRect(int x, int y, int w, int h, int startColor, int endColor)
	{	
		int rs = startColor >>>	16;
		int gs = (startColor >>> 8) & 0x000000FF;
		int bs = startColor & 0x000000FF;
				
		int rRatio = ((rs - (endColor >>> 16)) / (h)) * -1;
		int gRatio = ((gs - ((endColor >>> 8) & 0x000000FF)) / (h)) * -1;
		int bRatio = ((bs - (endColor & 0x000000FF)) / (h)) * -1;
				
		for(int i = 0; i < h; i++)
		{
			setColor((rs<<16) | (gs<<8) | (bs));
			fillRect(x, y + i, w, 1);
			rs+=rRatio;
			gs+=gRatio;
			bs+=bRatio;
		}		
	}
	
	/**
	 * Dibuja un rectangulo con alfa. Si outline es true dibuja una linea de borde.
	 * */
    void drawAlphaRect(int x, int y, int width, int height, int color, boolean outline)
    {
        int[] data = new int[width * height];
		
        for (int i=0; i < data.length; i++)
        {
            data[i] = color;
        }
		
        drawRGB(data, 0, width, x, y, width, height, true);
        
        if (outline)
        {
            int outlineColor = (color & 0x00FFFFFF);
            setColor(outlineColor);
            fillRect(0, y, width, 1);
            fillRect(0, y + height, width, 1);
        }
    }
    
    /**
     * Aplica el gradient a un color determinado, teniendo en cuenta el ancho que va tener el gradient (max) y de acuerdo a una
     * proporcion (prop).
     * */
    int gradColor(int color1, int color2, int prop, int max)
	{
		int red = 
			(((color1 >> 16) & 0xff) * prop +
			((color2 >> 16) & 0xff) * (max - prop)) / max;
 
		int green = 
			(((color1 >> 8) & 0xff) * prop +
			((color2 >> 8) & 0xff) * (max - prop)) / max;
 
		int blue = 
			(((color1 >> 0) & 0xff) * prop +
			((color2 >> 0) & 0xff) * (max - prop)) / max;
 
		int color = red << 16 | green << 8 | blue;
 
		return color;
	}

    /**
     * Da un efecto de un gradient de dos colores. Es medio raro pero esta bueno.
     * */
    int gradColorDouble(int c1, int c2, int prop, int h)
	{
		int r, g, b;
		int d1 = (r = GET_RED(c1)) - GET_RED(c2); 
		int d2 = (g = GET_GREEN(c1)) - GET_GREEN(c2); 
		int d3 = (b = GET_BLUE(c1))  - GET_BLUE(c2);
		
		d1 = MIN(MIN(d1,d2), d3) / h + 1;
		r -= d1 * prop;
		g -= d1 * prop;
		b -= d1 * prop;
		
		return (r << 16 | g << 8 | b);
	}
	
    /**
     * Este gradient en una cagada. No lo usen!! Algun dia lo arreglare y lo hare como corresponda.
     * Por el nombre que le puse iria bien a Jon.
     * */
	
	/**
	 * Hace una especie de gradient partido. Oscurece el color hasta la mitad y despues lo empieza a aclarar.
	 * */
	int gradColorBreak(int c1, int c2, int prop, int h)
	{
		int r1,r2,g1,g2,b1,b2;
		r1 = GET_RED(c1);
		r2 = GET_RED(c2);
		g1 = GET_GREEN(c1);
		g2 = GET_GREEN(c2);
		b1 = GET_BLUE(c1);
		b2 = GET_BLUE(c2);
		int d1 = r1 - GET_RED(c2); 
		int d2 = g1 - GET_GREEN(c2); 
		int d3 = b1 - GET_BLUE(c2);
		
		d1 = ABS(MIN(MIN(d1,d2), d3) / h) + 1;
		
		if ( prop < h / 2)
		{
			if (r1 > r2)
			{
				r1 -= d1 * prop;
			}
			else
			{
				r1 += d1 * prop;
			}
			
			if (g1 > g2)
			{
				g1 -= d1 * prop;
			}
			else
			{
				g1 += d1 * prop;
			}
			
			if (b1 > b2)
			{
				b1 -= d1 * prop;
			}
			else
			{
				b1 += d1 * prop;
			}
		}
		else
		{
			if (r1 < r2)
			{
				r1 -= d1 * prop;
			}
			else
			{
				r1 += d1 * prop;
			}
			
			if (g1 < g2)
			{
				g1 -= d1 * prop;
			}
			else
			{
				g1 += d1 * prop;
			}
			
			if (b1 < b2)
			{
				b1 -= d1 * prop;
			}
			else
			{
				b1 += d1 * prop;
			}
		}
		
		return (r1 << 16 | g1 << 8 | b1);
	}
    
    /**
     * Dibuja un marco con gradiente.
     * Dibuja de tal manera de no afectar el area interior al cuadro, es decir si se le pasa x,y = 10,10 w,h = 10,10 para que el area dentro del
     * cuadro quede de 10 x 10 lo q hace es desplazarse hacia afuera para empezar a pintar el cuadro, o sea lo que hace es un corrimiento de 
     * coordenadas. Corre a "x" y a "y" hacia atras y arriba respectivamente. <p/><b>x = x - bw; <br/>y = y - bw</b>;
     * @param x
     * @param y
     * @param w
     * @param h
     * @param bw Border width.
     * @param c1 Color externo del marco.
     * @param c2 Color interno del marco.
     *  
     * */
    void drawGradientFrame(int x, int y, int w, int h, int bw, int c1, int c2) 
    {
    	int c;
		
		for (int i = 0; i <= bw; i++) 
		{
			c = gradColor(c2, c1, i, bw);
			g.setColor(c);					
			drawRect(x - bw + i, y - bw + i, w + bw * 2 - i * 2, h + bw * 2 - i * 2);
		}
	}

    /**
     * Dibuja un marco con color plano.
     * Dibuja de tal manera de no afectar el area interior al cuadro, es decir si se le pasa x,y = 10,10 w,h = 10,10 para que el area dentro del
     * cuadro quede de 10 x 10 lo q hace es desplazarse hacia afuera para empezar a pintar el cuadro, o sea lo que hace es un corrimiento de 
     * coordenadas. Corre a "x" y a "y" hacia atras y arriba respectivamente. <p/><b>x = x - bw; <br/>y = y - bw</b>;
     * @param x
     * @param y
     * @param w
     * @param h
     * @param bw Border width.
     * @param c Color del marco.
     *  
     * */
    void drawFrame(int x, int y, int w, int h, int bw, int c) 
    {
		g.setColor(c);
    	for (int i = 0; i < bw; i++)
		{
    		drawRect(x - bw + i, y - bw + i, w + bw * 2 - i * 2, h + bw * 2 - i * 2);
    	}
    }
    
    /**
     * Dibuja un marco con borde.
     * Dibuja de tal manera de no afectar el area interior al cuadro, es decir si se le pasa x,y = 10,10 w,h = 10,10 para que el area dentro del
     * cuadro quede de 10 x 10 lo q hace es desplazarse hacia afuera para empezar a pintar el cuadro, o sea lo que hace es un corrimiento de 
     * coordenadas. Corre a "x" y a "y" hacia atras y arriba respectivamente. <p/><b>x = x - bw; <br/>y = y - bw</b>;
     * @param x
     * @param y
     * @param w
     * @param h
     * @param fw Frame width.
     * @param c Color del marco.
     *  
     * */
    void drawOutlinedFrame (int x, int y, int w, int h, int fw, int c)
	{
    	drawFrame(x, y, w, h, fw, fadeColor(c));
    	drawFrame(x+1, y+1, w-2, h-2, fw-2, c);
    }
    
	void setFont(int type)
	{
		if (!useLowFont)
		{
			fontHig.setFont(type);
		}
		else
		{
			fontLow.pixelSize = type;
		}
	}
	
	void drawString(String str, int x, int y, int anchor)
	{
		if (!useLowFont)
		{
			fontHig.drawString(str, x, y, anchor);
		}
		else
		{
			fontLow.drawString(str, x, y, anchor);
		}
	}
	
	byte drawString(String str, int x, int y, int maxWidth, int anchor)
	{
		if (!useLowFont)
		{
			return fontHig.drawString(str, x, y, maxWidth, anchor);
			 
		}
		else
		{
			fontLow.drawString(str, x, y, maxWidth, anchor);
			return 1; //TODO: corregir esto
		}
	}
	
	int getFontPixelSize()
	{
		if (!useLowFont)			
		{
			return fontHig.getFont();
		}
		else
		{
			return fontLow.pixelSize;
		}
	}
	
	int getStringWidth(String str)
	{
		if (!useLowFont)
		{
			return fontHig.getStringWidth(str);
		}
		else 
		{
			return fontLow.getStringWidth(str);
		}
	}
	
	int getStringHeight(String str, int maxWidth)
	{
		if (!useLowFont)
		{
			return fontHig.getStringHeight(str, maxWidth);
		}
		else
		{
			return fontLow.getStringHeight(str, maxWidth);
		}
	}
	
	int getFontHeight()
	{
		if (!useLowFont)
		{
			return fontHig.getFontHeight();
		}
		else 
		{
			return fontLow.getFontHeight();
		}
	}
	
	void setFontColor(int color)
	{
		if (!useLowFont)
		{
			fontHig.setColor(color);
		}
		else
		{
			fontLow.setColor(color);
		}
	}
	
	void setFontHLColor(int color)
	{
		if (!useLowFont)
		{
			fontHig.hlColor = color;
		}
		else
		{
			fontLow.hlColor = color;
		}
	}
	
	int getFontWidth()
	{
		if (!useLowFont)
		{
			return fontHig.getFontWidth();
		}
		else
		{
			return fontLow.getFontWidth();
		}
	}
	
	int fadeColor (int color)
	{
		int r = color >> 16;
		int g = (color >> 8) & 0x0000FF;
		int b = color & 0x0000FF;
		r = r >> 1;
		g = g >> 1;
		b = b >> 1;
		return ((r << 16) | (g << 8) | b);
	}

	int fadeColor (int color, int fadeLevel)
	{
		int r = color >> 16;
		int g = (color >> 8) & 0x0000FF;
		int b = color & 0x0000FF;
		int fl = (fadeLevel >> 1) > 0 ? (fadeLevel >> 1) : 1;
		r = r >> fl;
		g = g >> fl;
		b = b >> fl;
		return ((r << 16) | (g << 8) | b);
	}
	
	int highlightColor (int color)
	{
		int r = color >> 16;
		int g = (color >> 8) & 0x0000FF;
		int b = color & 0x0000FF;
		r = r << 1;
		g = g << 1;
		b = b << 1;
		return ((r << 16) | (g << 8) | b);
	}
	
	String[] split(String str, String splitChar)
	{	
		if (!useLowFont)
		{
			return fontHig.split(str, splitChar);
		}
		else
		{
			return fontLow.split(str, splitChar);
		}
	}
	
	public static Image stretchImageFromFile(String source, int destW, int destH)
	{
		Image img;
		try
		{
			img = Image.createImage("".getClass().getResourceAsStream(source));
		}
		catch(Exception e)
		{
			cGame.error = "Could not find Image " + source + " " + __FILE__ + " " + __LINE__;
			return null;
		}
		
		return stretchImage(img, destW, destH);
	}
	
	public static Image stretchImage(Image img, int destW, int destH)
	{
		return stretchImage(img, 0, 0, img.getWidth(), img.getHeight(), destW, destH);
	}
	
	public static Image stretchImage(Image img, int srcX, int srcY, int srcW, int srcH, int destW, int destH)
	{
		int[] srcRGB = new int[srcW * srcH];
		img.getRGB(srcRGB, 0, srcW, srcX, srcY, srcW, srcH);
		
		int[] destRGB = new int[destW * destH];
		
		int scanlength = img.getWidth();
		int acumX = 0;
		int acumY = 0;
		int srcOffsY = (srcY * scanlength) + srcX;
		int destOffs = 0;
		
		for (int y = 0; y < destH; y++)
		{
			int srcOffsX = srcOffsY;
			for (int x = 0; x < destW; x++)
			{
				destRGB[destOffs++] = srcRGB[srcOffsX];
				acumX += srcW;
				while (acumX >= destW)
				{
					srcOffsX++;
					acumX -= destW;
				}
			}
			acumY += srcH;
			while (acumY >= destH)
			{
				srcOffsY += scanlength;
				acumY -= destH;
			}
		}
		
		return Image.createRGBImage(destRGB, destW, destH, true);
	}
	
	public static Image resizeImage(Image img, int percentage)
	{
		int sourceWidth = img.getWidth(), sourceHeight = img.getHeight();
		int destWidth = sourceWidth * percentage / 100;
		int destHeight = sourceHeight * percentage / 100;
		
		return resizeImage(getImageRGB(img),sourceWidth , sourceHeight, destWidth, destHeight);
	}
	
	public static Image resizeImage(int[] srcRGB, int srcW, int srcH, int destW, int destH)
	{
		int[] destRGB = new int[destW * destH];
		
		int [] mappingX = new int[destW + 1];
		fillProportionalMapping(mappingX, srcW, destW);
		
		int [] mappingY = new int[destH + 1];
		fillProportionalMapping(mappingY, srcH, destH);
		
		int srcX = 0, srcY = 0;
		int destX = 0, destY = 0;
		int count = 0;
		
		while (true)
		{
			int srcOffs = (srcY * srcW) + srcX;
			int srcA = (srcRGB[srcOffs] & 0xFF000000) >> 24;
			
			int destOffs = (destY * destW) + destX;
			int destA = (destRGB[destOffs] & 0xFF000000) >> 24;
			int destR = (destRGB[destOffs] & 0x00FF0000) >> 16;
			int destG = (destRGB[destOffs] & 0x0000FF00) >> 8;
			int destB = (destRGB[destOffs] & 0x000000FF) >> 0;
			
			if (srcA != 0)
			{
				int srcR = (srcRGB[srcOffs] & 0x00FF0000) >> 16;
				int srcG = (srcRGB[srcOffs] & 0x0000FF00) >> 8;
				int srcB = (srcRGB[srcOffs] & 0x000000FF) >> 0;
				destA = ((destA * count) + srcA) / (count+1);
				destR = ((destR * count) + srcR) / (count+1);
				destG = ((destG * count) + srcG) / (count+1);
				destB = ((destB * count) + srcB) / (count+1);
			}
			count++;
			destRGB[destOffs] = (destA << 24) | (destR << 16) | (destG << 8) | destB;
			
			srcX++;
			if (srcX == mappingX[destX+1])
			{
				srcY++;
				if (srcY == mappingY[destY+1])
				{
					destX++;
					if (destX == destW)
					{
						destY++;
						if (destY == destH) break;
						destX = 0;
					}
					count = 0;
					srcY = mappingY[destY];
				}
				srcX = mappingX[destX];
			}
		}
		
		return Image.createRGBImage(destRGB, destW, destH, true);
	}
	
	public static void fillProportionalMapping(int[] mapping, int src, int dest)
	{
		int i = 0, j = 0, acum = 0;
		while (j < dest)
		{
			mapping[j++] = i;
			acum += src;
			while (acum >= dest)
			{
				i++;
				acum -= dest;
			}
		}
		mapping[dest] = src;
	}
	
	public static int[] getImageRGB(Image img)
	{
		return getImageRGB(img, 0, 0, img.getWidth(), img.getHeight());
	}
	
	public static int[] getImageRGB(Image img, int x, int y, int w, int h)
	{
		int[] srcRGB = new int[w * h];
		img.getRGB(srcRGB, 0, w, x, y, w, h);
		return srcRGB;
	}
	
	void drawArrow(int x1, int y1, int x2, int y2, int x3, int y3, int color, int shadowColor)
	{
		setColor(shadowColor);
		g.fillTriangle(x1 + 2, y1 + 3, x2 + 2, y2 + 3, x3 + 2, y3 + 3);
		setColor(color);
		g.fillTriangle(x1, y1, x2, y2, x3, y3);
	}
	
	void drawArrow(int x, int y, int w, int h, int color, int shadowColor, int orientation)
	{
		int top, base, left, midle, right;
		switch (orientation)
		{
			case ACTIONS.LEFT:
				top = x - (h >> 1);
				base = x + (h >> 1);
				left = y - (w >> 1);
				midle = y;
				right = y + (w >> 1);
				drawArrow(top, midle, base, right, base, left, color, shadowColor);
			break;

			case ACTIONS.RIGHT:
				top = x + (h >> 1);
				base = x - (h >> 1);
				left = y - (w >> 1);
				midle = y;
				right = y + (w >> 1);
				drawArrow(top, midle, base, right, base, left, color, shadowColor);
			break;

			case ACTIONS.UP:
				top = y - (h >> 1);
				base = y + (h >> 1);
				left = x - (w >> 1);
				midle = x;
				right = x + (w >> 1);
				drawArrow(midle, top, right, base, left, base, color, shadowColor);
			break;

			case ACTIONS.DOWN:
				top = y + (h >> 1);
				base = y - (h >> 1);
				left = x - (w >> 1);
				midle = x;
				right = x + (w >> 1);
				drawArrow(midle, top, right, base, left, base, color, shadowColor);
			break;

			default:
				top = x - (h >> 1);
				base = x + (h >> 1);
				left = y + (w >> 1);
				midle = y;
				right = y + (w >> 1);
			break;
		}
	}

	/**
	 * Dibuja un recuadro con determinado grosor.
	 * @param x.
	 * @param y.
	 * @param w.
	 * @param h.
	 * @param aw. El radio del arco en el eje x.
	 * @param ah. El radio del arco en el eje y.
	 * @param bw. El grosor del borde.
	 * */
	void drawRoundFrame(int x, int y, int w, int h, int aw, int ah, int bw)
	{
		for (int i = 0 ; i < bw ; ++i)
		{
			drawRoundRect(x - i, y - i, w + 2 * i , h + 2 * i , aw + i, ah + i);
		}
	}

	void drawRoundRect(int x, int y, int w, int h, int aw, int ah)
	{
		g.drawRoundRect(x, y, w, h, aw, ah);
	}
	
	void drawButton(String str, int x, int y, int w, int h, int c, int c2, int f, int f2, boolean selected, int strOffX)
	{
		drawButton(null, str, x, y, w, h, c, c2, f, f2, selected, strOffX);
	}
	
	void drawButton(String str, int x, int y, int w, int h, int c, int c2, int f, int f2, boolean selected)
	{
		drawButton(null, str, x, y, w, h, c, c2, f, f2, selected, 0);
	}
	
	void drawButton(Image icon, String str, int x, int y, int w, int h, int c, int c2, int f, int f2, boolean selected)
	{
		drawButton(icon, str, x, y, w, h, c, c2, f, f2, selected, 0);
	}
	
	// TODO: replace the image by a sprite, and then modify the method to receive the frame that should be drawn
	void drawButton(Image icon, String str, int x, int y, int w, int h, int c, int c2, int f, int f2, boolean selected, int strOffX)
	{
		if (!selected) 
		{
			setColor(c);
			setFontColor(f);
		}
		else
		{
			setColor(c2);
			setFontColor(f2);
		}
		
		fillRect(x, y, w, h); // Primero dibujo el boton con color plano.
		
		if(icon == null)
		{
			saveClip();
			setClip(x + (w >> 4), y, w - (w >> 4), h);
			
			drawString(str, x + (w >> 4) + strOffX, y + (h - (h >> 2)), DSmallFont.BOTTOM | DSmallFont.LEFT ); // El texto del boton.			
			
			restoreClip();
		}
		else
		{			
			x += (icon.getWidth() / 2);
			drawImage(icon, x, y + (h >> 1) - (icon.getHeight() >> 1), 0);
			x += (icon.getWidth() + icon.getWidth() / 2);
			
			saveClip();
			setClip(x, y, w - (icon.getWidth() * 2), h);;
			
			drawString(str, x + strOffX, y + (h - (h >> 2)), DSmallFont.BOTTOM | DSmallFont.LEFT);
			
			restoreClip();
		}
	}
	
	public void drawFancyButton (String str, int x, int y, int w, int h, boolean outLine, boolean selected)
	{
		drawFancyButton (str, x, y, w, h, 0x808080, 0x202020, outLine, selected);
	}

	public void drawFancyButton (int x, int y, int w, int h, boolean outLine, boolean selected)
	{
		drawFancyButton ("", x, y, w, h, 0x808080, 0x202020, outLine, selected);
	}
	
	public void drawFancyButton (String str, int x, int y, int w, int h, int colorUp, int colorDown, boolean outLine, boolean selected)
	{
		int r, g, b, k;
		
		//Draw button background
		for (int i = 0; i < h; i++) 
		{
			if (i < h/2)
			{
				k = 170 - (selected ? 70 : 0) - ( 200 / h) * i;
			} 
			else
			{
				k = 20 + ((2 * i - h) * 80) / h;
			}
			 
			r = (k + (selected ? 40 : 0));
			g = (k + (selected ? 40 : 0));
			b = (k + (selected ? 0 : 20));
			
			setColor(r << 16 | g << 8 | b);
			
			drawLine(x, y + i, x + w, y + i);
		}
		
		if (selected)
		{
			setColor(colorUp);
		}
		else
		{
			setColor(colorDown);
		}
		
		if (outLine)
		{
			drawRect(x, y, w, h);
		}
		
		drawString(str, x + (w >> 1), y + (h - (h >> 2)), DSmallFont.BOTTOM | DSmallFont.HCENTER );
	}
	
	/**
	 * Dibuja un boton con un motivo similar a windows Vista. 
	 * Y ya lo ve, y ya lo ve, es para Jhon que lo mira por TV!!
	 * */
	void drawVistaButton (String str, int x, int y, int w, int h, int c, int c2, int f, int f2, boolean selected)
	{
		if (selected) 
		{
			setColor(c2);
			setFontColor(f2);
		}
		else
		{
			setColor(c);
			setFontColor(f);
		}
		
		fillRect(x, y, w, h); // Primero dibujo el boton con color plano.
		
		drawGFRect(x, y, w, h / 2, highlightColor((selected)? c2 : c), (selected)? c2 : c);
		
		drawGFRect(x, y + h / 2, w, h / 2, fadeColor((selected)? c2 : c), (selected)? c2 : c);

		if(selected)
		{
			setColor(c2);
		}
		else
		{
			setColor(c);
		}
		
		drawRect(x, y, w - 1, h);
		
		drawString(str, x + (w >> 1), y + (h - (h >> 2)), DSmallFont.BOTTOM | DSmallFont.HCENTER ); // El texto del boton.
	}
	
	void drawVistaButton (String str, int x, int y, int w, int h, int c, int c2, boolean selected)
	{
		drawVistaButton(str, x, y, w, h, c, c2, EMidFont.WHITE, EMidFont.YELLOW, selected);
	}
	
	/**
	 * Devuelve solo el canal mas alto de un color en formato RGB.
	 * */
	int getHighestChannel (int c) 
	{
		int r,g,b;
		r = GET_RED(c) != 0 ? GET_RED(c) : 1;
		g = GET_GREEN(c) != 0 ? GET_GREEN(c) : 1;

		b = GET_BLUE(c) != 0 ? GET_BLUE(c) : 1;
		if (r > g)
		{
			if (r > b)
			{
				return r << 16 | r / g << 8 | r / b;
			}
			else
			{
				return b / r << 16 | b / g << 8 | b;
			}
		}
		else
		{
			if (g > b)
			{
				return g / r << 16 |  g << 8 | g / b;	
			} 
			else 
			{ 
				return b / r << 16 | b / g << 8 | b; 
			}
		}
	}
	
	void drawInputTextBox (int x, int y, int w, int h, int bgc, int ftc, boolean outline, boolean round) 
	{
		drawTextContainer(x, y, w, h, 0, 0, bgc, ftc);

		setFontColor(ftc);
		
		// Calculo los margenes para el texto.
		int marginX = getFontWidth() << 1;
		int marginY = getFontHeight();
		int maxWidth = w - (marginX << 1);
		int maxHeight = h - (marginY << 1);
		// Llamo a dibujar con limite de ancho y alto.
		if(cGame.currInputText != null)
		{
			if (useLowFont)
			{
				fontLow.drawString(cGame.currInputText, 0, x + marginX, y + marginY, maxWidth, maxHeight, DSmallFont.LEFT | DSmallFont.TOP);
			}
			else
			{
				fontHig.drawString(cGame.currInputText, 0, x + marginX, y + marginY, maxWidth, maxHeight, DSmallFont.LEFT | DSmallFont.TOP);
			}
		}
	}

	/**
	 * Draws a text box.
	 * @param text The Text to be drawn.
	 * @param sl Start Line. The first line of the text that is going to be drawn.
	 * @param x The left edge of the text box.
	 * @param y The top edge of the text box.
	 * @param w The text box width.
	 * @param h The text box height.
	 * @param ar The arc adius, cause the text box is a round rect. If it is 0 the text box will be rect.
	 * @param bw Border width. The width of the frame. If 0 there wont be any frame.
	 * @param bgc Back ground color.
	 * @param ftc Font color.
	 * 
	 * */
	void drawDisplayTextBox (String text, int sl, int x, int y, int w, int h, int ar, int bw, int bgc, int ftc) 
	{
		drawTextContainer(x, y, w, h, ar, bw, bgc, ftc);

		setFontColor(ftc);
		
		// Calculo los margenes para el texto.
		int marginX = getFontWidth() << 1;
		int marginY = getFontHeight();
		int maxWidth = w - (marginX << 1);
		int maxHeight = h - (marginY << 1);
		// Llamo a dibujar con limite de ancho y alto.
		if (useLowFont)
		{
			fontLow.drawString(text, sl, x + marginX, y + marginY, maxWidth, maxHeight, DSmallFont.LEFT | DSmallFont.TOP);
		}
		else
		{
			fontHig.drawString(text, sl, x + marginX, y + marginY, maxWidth, maxHeight, DSmallFont.LEFT | DSmallFont.TOP);
		}
	}
	
	void drawDisplayTextBox (String text, int x, int y, int w, int h, int ar, int bw, int bgc, int ftc) 
	{
		drawDisplayTextBox (text, 0, x, y, w, h, ar, bw, bgc, ftc);
	}
	
	/**
	 * Dibuja un contenedor de texto. Usa x, y, w, h para las dimensiones, usa ar (arc radius) y bw (border width) para el redondeado de los vertices 
	 * y ancho del recuadro del contenedor y usa bgc (background color) y ftc (font color) para la estetica. 
	 * */
	void drawTextContainer (int x, int y, int w, int h, int ar, int bw, int bgc, int ftc)
	{
		g.setColor(bgc);
		if (ar != 0) // El radio no es cero. Quiere que sea redondeado!
		{
			g.fillRoundRect(x, y, w, h, ar, ar);
		}
		else 
		{
			g.fillRect(x, y, w, h);
		}
		if (bw != 0) // El grosor del borde no es 0. Quiere borde!
		{
			if (ar != 0) // El radio no es cero. Quiere que sea redondeado!
			{
				drawRoundFrame(x, y, w, h, ar, ar, bw);
			}
			else
			{
				drawGradientFrame(x, y, w, h, bw, ftc, bgc);
			}
		}
	}
	
    public Image loadImage(String location)
	{
		Image myImage = null;
		try
		{
			myImage = Image.createImage("".getClass().getResourceAsStream(location));
		}
		catch(Exception e)
		{
			cGame.error = "Error loading image: " + location + " in " + __FILE__ + " " + __LINE__;
        }
		return myImage;
	}
	
}